﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using BingChat;
using chen_bot_ui.Models;
using chen_bot_ui.Views;
using Microsoft.EntityFrameworkCore;
using ReactiveUI;

namespace chen_bot_ui.ViewModels
{
    public class ChatViewModel : ViewModelBase
    {
        private string _dbPath, _fileButtonContent, _gptKey;
        private bool _running, _dbPathIsReadonly, _gptRunning;
        private ReactiveCommand<Unit, Unit> _fileButtonCommand;
        public ReactiveCommand<Unit, Unit> GptSumCommand { get; }
        public ReactiveCommand<Unit, Unit> GptInformationCommand { get; }
        public ReactiveCommand<Unit, Unit> GptReplyCommand { get; }

        public ReactiveCommand<Unit, Unit> FileButtonCommand
        {
            get => _fileButtonCommand;
            set => this.RaiseAndSetIfChanged(ref _fileButtonCommand, value);
        }

        private List<ChatMsg> _chatMsgs = new();
        private FileSystemWatcher? _fileSystemWatcher;

        public string DbPath
        {
            get => _dbPath;
            set => this.RaiseAndSetIfChanged(ref _dbPath, value);
        }

        public string FileButtonContent
        {
            get => _fileButtonContent;
            set => this.RaiseAndSetIfChanged(ref _fileButtonContent, value);
        }

        public string GptKey
        {
            get => _gptKey;
            set => this.RaiseAndSetIfChanged(ref _gptKey, value);
        }

        private bool Running
        {
            get => _running;
            set => this.RaiseAndSetIfChanged(ref _running, value);
        }

        private bool GptRunning
        {
            get => _gptRunning;
            set => this.RaiseAndSetIfChanged(ref _gptRunning, value);
        }

        public bool DbPathIsReadonly
        {
            get => _dbPathIsReadonly;
            set => this.RaiseAndSetIfChanged(ref _dbPathIsReadonly, value);
        }

        public List<ChatMsg> ChatMsgs
        {
            get => _chatMsgs;
            set => this.RaiseAndSetIfChanged(ref _chatMsgs, value);
        }

        public List<ChatMsg> SelectedChatMsgs { get; } = new();

        public ChatViewModel()
        {
            _fileButtonContent = _dbPath = _gptKey = string.Empty;
            this.WhenAnyValue(x => x.DbPath, x => x.Running,
                (dbPath, running) => running ? ButtonType.Stop :
                    string.IsNullOrEmpty(dbPath) ? ButtonType.Select : ButtonType.Start
            ).Subscribe(SelectButton);
            SelectButton(ButtonType.Select);

            // GPT 按钮是否可用的可观察值
            var gptButtonValid = this.WhenAnyValue(x => x.GptKey, x => x.GptRunning, x => x.Running,
                (gptKey, gptRunning, running) => !string.IsNullOrEmpty(gptKey) && !gptRunning && running);

            GptSumCommand = ReactiveCommand.CreateFromTask(() => AskGpt(AskType.Sum), gptButtonValid);
            GptInformationCommand = ReactiveCommand.CreateFromTask(() => AskGpt(AskType.Information), gptButtonValid);
            GptReplyCommand = ReactiveCommand.CreateFromTask(() => AskGpt(AskType.Reply), gptButtonValid);
        }

        enum ButtonType
        {
            Select,
            Start,
            Stop,
        };

        private void SelectButton(ButtonType type)
        {
            switch (type)
            {
                case ButtonType.Select:
                    FileButtonCommand = ReactiveCommand.CreateFromTask(SelectDb);
                    FileButtonContent = "选择数据库";
                    DbPathIsReadonly = false;
                    break;
                case ButtonType.Start:
                    FileButtonCommand = ReactiveCommand.CreateFromTask(Start);
                    FileButtonContent = "开始读数据";
                    DbPathIsReadonly = false;
                    break;
                case ButtonType.Stop:
                    FileButtonCommand = ReactiveCommand.Create(Stop);
                    FileButtonContent = "终止读数据";
                    DbPathIsReadonly = true;
                    break;
            }
        }

        private async Task SelectDb()
        {
            var dialog = new OpenFileDialog
            {
                AllowMultiple = false,
                Title = "Select a file",
                Directory = Environment.CurrentDirectory,
                Filters = new List<FileDialogFilter>
                {
                    new()
                    {
                        Name = "SqLite3 Database",
                        Extensions = new List<string> { "db", "sqlite3" }
                    },
                    new()
                    {
                        Name = "All",
                        Extensions = new List<string> { "*" }
                    }
                }
            };
            var result = await dialog.ShowAsync(MainWindow.Instant);
            if (result != null)
            {
                DbPath = result[0];
            }
        }

        private async Task Start()
        {
            await LoadChatMsgDb();
            _fileSystemWatcher = new()
            {
                Filter = Path.GetFileName(DbPath),
                Path = Path.GetDirectoryName(DbPath) ?? string.Empty,
                IncludeSubdirectories = false,
            };
            _fileSystemWatcher.Changed += async (_, _) => { await LoadChatMsgDb(); };
            _fileSystemWatcher.EnableRaisingEvents = true;
            Running = true;
        }

        private int _readMsgNum = 100;
        private async Task LoadChatMsgDb()
        {
            var chatMsgDb = ChatMsgDb.CreateChatMsgDbConnect(DbPath);
            if (chatMsgDb is null)
            {
                DbPath = "数据库读取失败";
                return;
            }

            // TakeLast 不能翻译成 SQL 语句，故不能使用，因此需要用其他方法
            ChatMsgs = await chatMsgDb.ChatMsg.Skip(int.Max(0, await chatMsgDb.ChatMsg.CountAsync() - _readMsgNum)).Take(_readMsgNum)
                .ToListAsync();
        }

        private void Stop()
        {
            if (_fileSystemWatcher != null)
            {
                _fileSystemWatcher.EnableRaisingEvents = false;
                _fileSystemWatcher.Dispose();
            }

            Running = false;
        }

        enum AskType
        {
            Sum,
            Information,
            Reply,
        };

        private async Task AskGpt(AskType askType)
        {
            GptRunning = true;

            StringBuilder sb = new();
            sb.Append("接下来我会给出一段聊天记录，");
            switch (askType)
            {
                case AskType.Sum:
                    sb.Append("请总结其中的内容：");
                    break;
                case AskType.Information:
                    sb.Append("请将其中的可能难懂的信息扩展一下或者介绍一下：");
                    break;
                case AskType.Reply:
                    sb.Append("请以我作为其中一个用户的角度给出对于这些内容可能的回复：");
                    break;
            }

            foreach (ChatMsg chatMsg in SelectedChatMsgs)
            {
                sb.Append($"用户{chatMsg.UserQQ}说：'{chatMsg.Context}'");
            }

            var client = new BingChatClient(new BingChatClientOptions
            {
                CookieU = GptKey,
                Tone = BingChatTone.Creative,
            });
            var conversation = await client.CreateConversation();
            var _ = await conversation.AskAsync("你好！");

            var res = await conversation.AskAsync(sb.ToString());
            GptRunning = false;

            Window resWindow = new ChatResWindow()
            {
                DataContext = new ChatResViewModel(res),
            };
            resWindow.Show(MainWindow.Instant);
        }

    }
}
